package com.lancoo.ccas52.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.lancoo.ccas52.entity.*;
import com.lancoo.ccas52.mapper.*;
import com.lancoo.ccas52.pojo.common.PageInfo;
import com.lancoo.ccas52.pojo.constant.GlobalConstant;
import com.lancoo.ccas52.pojo.dto.AllScheduleDto;
import com.lancoo.ccas52.pojo.dto.ScheduleDto;
import com.lancoo.ccas52.pojo.dto.SyncScheduleDto;
import com.lancoo.ccas52.pojo.dto.TeachingClassDto;
import com.lancoo.ccas52.pojo.vo.*;
import com.lancoo.ccas52.service.*;
import com.lancoo.ccas52.util.ExcelFormatUtil;
import com.lancoo.ccas52.util.PagingUtil;
import com.lancoo.ccas52.util.WeekUtil;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Component
public class TimetableServiceImpl implements TimetableService {

    @Resource
    private ScheduleMapper scheduleMapper;

    @Resource
    private TeachingClassTeacherMapper teachingClassTeacherMapper;
    @Resource
    private TeachingClassStudentMapper teachingClassStudentMapper;

    @Resource
    private RoomMapper roomMapper;
    @Resource
    private TeachingClassService teachingClassService;
    @Resource
    private TaskInfoService taskInfoService;
    @Resource
    private TeacherService teacherService;
    @Resource
    private RuleWeekDayService ruleWeekDayService;
    @Resource
    private StudentService studentService;
    @Resource
    private BasicRuleService basicRuleService;
    @Resource
    private StudentMapper studentMapper;
    @Resource
    private RoomTypeService roomTypeService;


    @Override
    public PageInfo<TeachingClassCourseDto> getSchoolSchedule(IPage page, Integer taskId, String courseTypeId, Integer studyNature, Integer courseNature, Integer majorProp, String collegeId, String keyWord) {
        PageInfo<TeachingClassCourseDto> teachingClassPage = teachingClassService.getTeachingClass(page, taskId, courseTypeId, studyNature, courseNature, majorProp, collegeId, null, keyWord, null);
        return teachingClassPage;
    }

    @Override
    public PageInfo<TeacherStatistics> getTeacherStatistics(Integer taskId, String schoolId, String term, Integer currentPage, Integer pageSize, String collegeId, String keyWord) {
        List<TeacherStatistics> teacherStatisticsList = new ArrayList<>();

        Page<Teacher> page = new Page<>(currentPage, pageSize);
        PageInfo<Teacher> teacherPage = teacherService.getTeacherList(taskId, schoolId, term, page, collegeId, keyWord);
        List<Teacher> teacherList = teacherPage.getList();
        List<Long> teacherIds = teacherList.stream().map(Teacher::getTeacherId).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(teacherIds)) {
            //先只显示一个任务的统计信息
            Map<Long, List<TeachingClassDto>> teachingMap = teacherService.getTeachingClassByTeacherId(teacherIds).stream()
                    .filter(teachingClass -> teachingClass.getTaskId().equals(taskId))
                    .collect(Collectors.groupingBy(TeachingClassDto::getTeacherId));
            teacherList.forEach(teacher -> {
                TeacherStatistics teacherStatistics = new TeacherStatistics();
                BeanUtil.copyProperties(teacher, teacherStatistics);
                List<TeachingClassDto> teachingClassList = teachingMap.get(teacher.getTeacherId());
                if (CollUtil.isNotEmpty(teachingClassList)) {
                    //统计数量
                    long courseTeachingCount = teachingClassList.stream().filter(teachingClass -> teachingClass.getFlag() == 1).count();
                    long courseCount = teachingClassList.stream().filter(teachingClass -> teachingClass.getFlag() == 1)
                            .map(TeachingClassDto::getCourseId).distinct().count();
                    long segmentTeachingCount = teachingClassList.stream().filter(teachingClass -> teachingClass.getFlag() == 2).count();
                    long segmentCount = teachingClassList.stream().filter(teachingClass -> teachingClass.getFlag() == 2)
                            .map(TeachingClassDto::getCourseId).distinct().count();
                    teacherStatistics.setCourseTeachingCount(courseTeachingCount);
                    teacherStatistics.setCourseCount(courseCount);
                    teacherStatistics.setSegmentTeachingCount(segmentTeachingCount);
                    teacherStatistics.setSegmentCount(segmentCount);

                }
                teacherStatisticsList.add(teacherStatistics);
            });

        }
        PageInfo<TeacherStatistics> pageInfo = new PageInfo<>();
        return pageInfo.setList(teacherStatisticsList)
                .setTotal(teacherPage.getTotal())
                .setPages(teacherPage.getPages())
                .setCurrentPage(teacherPage.getCurrentPage())
                .setPageSize(teacherPage.getPageSize());
    }

    @Override
    public List<TeachingClassScheduleDto> getTeacherSchedule(Integer taskId, Long teacherId) {
        List<TeachingClassScheduleDto> teachingClassScheduleList = new ArrayList<>();
        //获取教师都有哪些教学班
        List<Long> classIds = teachingClassTeacherMapper.selectList(new LambdaQueryWrapper<TeachingClassTeacher>().eq(TeachingClassTeacher::getTeacherId, teacherId))
                .stream().map(TeachingClassTeacher::getTeachingClassId).distinct().collect(Collectors.toList());
        Page<TeachingClassCourseDto> page = new Page<>(1, -1);
        PageInfo<TeachingClassCourseDto> teachingClassPage = teachingClassService.getTeachingClass(page, taskId, null, null, null, null, null, classIds, null, null);
        teachingClassScheduleList.addAll(BeanUtil.copyToList(teachingClassPage.getList(), TeachingClassScheduleDto.class));

        //环节课表转化
        Page<TeachingClassSegmentDto> pageSegment = new Page<>(1, -1);
        PageInfo<TeachingClassSegmentDto> teachingClassSegmentPage = teachingClassService.getSegmentTeachingClass(pageSegment, taskId, null, null, null, classIds, null);
        teachingClassSegmentPage.getList().forEach(a -> {
            teachingClassScheduleList.add(new TeachingClassScheduleDto().setTeachingClassId(a.getTeachingClassId())
                    .setTeachingClassName(a.getTeachingClassName())
                    .setCourseId(a.getSegmentId())
                    .setCourseName(a.getSegmentName())
                    .setCourseTypeId(a.getSegmentTypeId())
                    .setCourseType(a.getSegmentType())
                    .setStudyNature(a.getStudyNature())
                    .setCourseWeeks(a.getCourseWeeks())
                    .setTeachers(a.getTeachers()));
        });
        return teachingClassScheduleList;
    }

    @Override
    public List<TeachingClassCourseDto> getRoomSchedule(Integer taskId, Long roomId) {
        //获取教室都有哪些教学班
        List<Long> classIds = scheduleMapper.selectList(new LambdaQueryWrapper<Schedule>().eq(Schedule::getRoomId, roomId))
                .stream().map(Schedule::getTeachingClassId).distinct().collect(Collectors.toList());
        Page<TeachingClassCourseDto> page = new Page<>(1, -1);
        PageInfo<TeachingClassCourseDto> teachingClassPage = teachingClassService.getTeachingClass(page, taskId, null, null, null, null, null, classIds, null, roomId);
        //过滤掉不是本教室的课表


        List<TeachingClassCourseDto> resultList = teachingClassPage.getList().stream().filter(teachingClassCourseDto -> {
            List<ScheduleDto> scheduleList = teachingClassCourseDto.getSchedules();
            if (CollUtil.isNotEmpty(scheduleList)) {
                return scheduleList.stream().anyMatch(schedule -> schedule.getRoomId().equals(roomId));
            }
            return false;
        }).collect(Collectors.toList());
        return resultList;
    }

    @Override
    public PageInfo<ClassTeachingClassDto> getClassList(Integer taskId, Integer currentPage, Integer pageSize, String collegeId, String keyWord) {
        Page<ClassTeachingClassDto> page = new Page<>(currentPage, pageSize);
        IPage<ClassTeachingClassDto> classTeachingClassDto = studentMapper.getClassTeachingClassDto(page, taskId, collegeId, keyWord);
        List<ClassTeachingClassDto> records = classTeachingClassDto.getRecords();
        if (CollUtil.isNotEmpty(records)) {
            List<String> classIds = records.stream().map(ClassTeachingClassDto::getClassId).collect(Collectors.toList());
            Map<String, List<TeachingClassToClassDto>> teachingClassMap = teachingClassService.getTeachingClassByClass(taskId, classIds).stream()
                    .collect(Collectors.groupingBy(TeachingClassToClassDto::getToClass));
            records.forEach(a -> {
                a.setTeachingClassList(teachingClassMap.get(a.getClassId()));
            });
        }
        return PagingUtil.formatPageResult(classTeachingClassDto);
    }

    @Override
    public List<TeachingClassCourseDto> getStudentSchedule(Integer taskId, Long studentId) {
        //获取学生都有哪些教学班
        List<Long> classIds = teachingClassStudentMapper.selectList(new LambdaQueryWrapper<TeachingClassStudent>().eq(TeachingClassStudent::getStudentId, studentId).eq(TeachingClassStudent::getFlag, 1))
                .stream().map(TeachingClassStudent::getTeachingClassId).distinct().collect(Collectors.toList());
        Page<TeachingClassCourseDto> page = new Page<>(1, -1);
        PageInfo<TeachingClassCourseDto> teachingClassPage = teachingClassService.getTeachingClass(page, taskId, null, null, null, null, null, classIds, null, null);
        return teachingClassPage.getList();
    }


    @Override
    public String schoolScheduleToExcel(Integer taskId, String courseTypeId, String courseType, Integer studyNature, Integer courseNature, String collegeId, String collegeName) {
        TaskInfo taskInfo = taskInfoService.getById(taskId);
        String storePath = "/" + taskInfo.getTaskName() + "/全校课表-" + LocalDate.now() + ".xlsx";
        String targetPath = GlobalConstant.OUTPUT_EXCEL_PATH + storePath;
        FileUtil.del(targetPath);
        ExcelWriter writer = ExcelUtil.getWriter(targetPath);

        Page<TeachingClassCourseDto> page = new Page<>(1, -1);
        PageInfo<TeachingClassCourseDto> teachingClassPage = teachingClassService.getTeachingClass(page, taskId, courseTypeId, studyNature, courseNature, null, collegeId, null, null, null);
        List<TeachingClassCourseDto> resultList = teachingClassPage.getList();
        String title = "{}课表(全校)  \n  {}";
        writer.merge(11, StrUtil.format(title, taskInfo.getSchoolName(), taskInfo.getTermName()), false);
        writer.setRowHeight(0, 40);
        String filterTem = "开课单位:{}  课程类别:{}  课程性质:{}  修读性质:{}   共:{}个教学班   导出时间:{} ";
        String filter = StrUtil.format(filterTem, StrUtil.isBlank(collegeId) ? "全部院系  " : collegeName,
                StrUtil.isBlank(courseTypeId) ? "全部  " : courseType,
                ObjUtil.isNull(courseNature) ? "全部" : (courseNature == 1 ? "必修" : "选修"),
                ObjUtil.isNull(studyNature) ? "全部" : (studyNature == 1 ? "主修" : "辅修"),
                resultList.size(), DateUtil.now());
        writer.merge(11, filter, false);
        List<List<String>> rows = new ArrayList<>();
        rows.add(Arrays.asList("代码", "名称", "总学时", "讲授学时", "实验学时", "上机学时", "其他学时", "教学班名称", "任课教师", "周次(小周)", "周学时分布(讲授-实验-上机-其他)", "场地节次"));
        List<String> row;
        Map<Integer, String> roomTypeMap = roomTypeService.lambdaQuery().eq(RoomType::getSchoolId, taskInfo.getSchoolId())
                .eq(RoomType::getTerm, taskInfo.getTerm()).list()
                .stream().collect(Collectors.toMap(RoomType::getId, RoomType::getTypeName));
        for (TeachingClassCourseDto item : resultList) {
            //任课教师名称
            String teacherName = ExcelFormatUtil.getTeacherName(item.getTeachers());
            //周学时
            String weekDistribution = ExcelFormatUtil.getWeekHourString(item.getCourseWeeks(),roomTypeMap);
            //周次
            String weeks = ExcelFormatUtil.getWeekString(item.getCourseWeeks());
            //场地节次
            String siteSection = ExcelFormatUtil.getSiteSection(item.getSchedules());
            row = Arrays.asList(item.getCourseCode(), item.getCourseName(), item.getTotalHour().toString(), item.getLectureHour().toString(),
                    item.getExperimentalHour().toString(), item.getComputerHour().toString(), item.getOtherHour().toString(),
                    item.getTeachingClassName(), teacherName, weeks, weekDistribution, siteSection);
            rows.add(row);
        }
        writer.write(rows, false);
        ExcelFormatUtil.setFont(writer);
        writer.close();
        return GlobalConstant.ACCESS_PATH + "/static/OutputExcel" + storePath;
    }

    @Override
    public String schoolSegmentScheduleToExcel(Integer taskId, String courseTypeId, String courseType, Integer studyNature, String collegeId, String collegeName) {

        TaskInfo taskInfo = taskInfoService.getById(taskId);
        String storePath = "/" + taskInfo.getTaskName() + "/环节全校课表-" + LocalDate.now() + ".xlsx";
        String targetPath = GlobalConstant.OUTPUT_EXCEL_PATH + storePath;

        FileUtil.del(targetPath);
        ExcelWriter writer = ExcelUtil.getWriter(targetPath);

        Page<TeachingClassSegmentDto> page = new Page<>(1, -1);
        PageInfo<TeachingClassSegmentDto> teachingClassPage = teachingClassService.getSegmentTeachingClass(page, taskId, courseTypeId, studyNature, collegeId, null, null);
        List<TeachingClassSegmentDto> resultList = teachingClassPage.getList();
        String title = "{}环节课表(全校)  \n  {}";
        writer.merge(6, StrUtil.format(title, taskInfo.getSchoolName(), taskInfo.getTermName()), false);
        writer.setRowHeight(0, 40);
        String filterTem = "开课单位:{}  环节类别:{}  修读性质:{}   共:{}个教学班   导出时间:{} ";
        String filter = StrUtil.format(filterTem, StrUtil.isBlank(collegeId) ? "全部院系  " : collegeName,
                StrUtil.isBlank(courseTypeId) ? "全部  " : courseType,
                ObjUtil.isNull(studyNature) ? "全部" : (studyNature == 1 ? "主修" : "辅修"),
                resultList.size(), DateUtil.now());
        writer.merge(6, filter, false);
        List<List<String>> rows = new ArrayList<>();
        rows.add(Arrays.asList("代码", "环节名称", "教学班名称", "任课教师", "班级人数", "环节类型", "周次(小周)"));
        List<String> row;
        for (TeachingClassSegmentDto item : resultList) {
            //任课教师名称
            String teacherName = ExcelFormatUtil.getTeacherName(item.getTeachers());
            //周次
            String weeks = ExcelFormatUtil.getWeekString(item.getCourseWeeks());
            row = Arrays.asList(item.getSegmentCode(), item.getSegmentName(), item.getTeachingClassName(), teacherName, item.getStudentNumber().toString(), item.getSegmentType(), weeks);
            rows.add(row);
        }
        writer.write(rows, false);
        ExcelFormatUtil.setFont(writer);
        writer.close();
        return GlobalConstant.ACCESS_PATH + "/static/OutputExcel" + storePath;
    }

    @Override
    public String teacherScheduleToExcel(Integer taskId, Long teacherId) {
        TaskInfo taskInfo = taskInfoService.getById(taskId);
        String storePath = "/" + taskInfo.getTaskName() + "/教师课表-" + LocalDate.now() + ".xlsx";
        String targetPath = GlobalConstant.OUTPUT_EXCEL_PATH + storePath;

        FileUtil.del(targetPath);
        ExcelWriter writer = ExcelUtil.getWriter(targetPath);

        Teacher teacher = teacherService.getById(teacherId);
        //获取教师都有哪些教学班
        List<Long> classIds = teachingClassTeacherMapper.selectList(new LambdaQueryWrapper<TeachingClassTeacher>().eq(TeachingClassTeacher::getTeacherId, teacherId))
                .stream().map(TeachingClassTeacher::getTeachingClassId).distinct().collect(Collectors.toList());
        Page<TeachingClassCourseDto> page = new Page<>(1, -1);
        PageInfo<TeachingClassCourseDto> teachingClassPage = teachingClassService.getTeachingClass(page, taskId, null, null, null, null, null, classIds, null, null);
        List<TeachingClassCourseDto> resultList = teachingClassPage.getList();
        //根据最大周次生成课表模板
        int periods = basicRuleService.getMaxPeriodScheme(taskInfo.getSchoolId(), taskInfo.getTerm()).getPeriodSchemes().size();
        String title = "{}教师课表  \n  {}";
        writer.merge(7, StrUtil.format(title, teacher.getTeacherName(), taskInfo.getTermName()), false);
        writer.setRowHeight(0, 40);
        long courseNum = resultList.stream().map(TeachingClassCourseDto::getCourseId).distinct().count();
        long sectionNum = resultList.stream()
                .flatMap(studentTeachingClass -> studentTeachingClass.getSchedules().stream())
                .filter(schedule -> ObjUtil.isNotNull(schedule.getTimeCode())).count();
        String filterTem = "{}上课   共:{}门课程，{}节课   导出时间:{} ";
        String filter = StrUtil.format(filterTem, taskInfo.getWeekSet().equals(0) ? "小周 " : "大周", courseNum, sectionNum, DateUtil.now());
        writer.merge(7, filter, false);
        List<List<String>> rows = ExcelFormatUtil.getScheduleFormat(periods, 7);
        for (TeachingClassCourseDto item : resultList) {
            //以及排好的课节
            List<ScheduleDto> arrangeSchedule = item.getSchedules().stream().filter(scheduleDto -> StrUtil.isNotBlank(scheduleDto.getTimeCode())).collect(Collectors.toList());
            //根据授课类型分组 使用TreeMap来排序
            TreeMap<Integer, List<ScheduleDto>> mapByHourType = arrangeSchedule.stream()
                    .collect(Collectors.groupingBy(ScheduleDto::getHourType, TreeMap::new, Collectors.toList()));
            mapByHourType.forEach((hourType, byHourTypeList) -> {
                //根据timeCode分组
                byHourTypeList.stream().collect(Collectors.groupingBy(ScheduleDto::getTimeCode)).forEach((timeCode, byTimeCodeList) -> {
                    //根据week分组
                    byTimeCodeList.stream().collect(Collectors.groupingBy(ScheduleDto::getWeek)).forEach((week, byWeekList) -> {
                        //单个教学班 单个讲授类型 单个周次  单个节次 对应字符串
//                            String scheduleHour = "{courseName}({hourType}) \n{teachingClassName}\n{buildingName}{roomName}\n{week}"
                        String scheduleHour = "{}({}) \n{}\n{}{}\n{}";
                        //key 0101  节次
                        int index = Integer.parseInt(timeCode.substring(2, 4));
                        String buildingName = byWeekList.get(0).getBuildingName();
                        String roomName = byWeekList.get(0).getRoomName();
                        String weekString = StrUtil.format("[{}]周", WeekUtil.binaryToString(week));
                        String format = StrUtil.format(scheduleHour, item.getCourseName(), GlobalConstant.hourTypeMap.get(hourType)
                                , item.getTeachingClassName(), buildingName, roomName, weekString);
                        rows.get(index).set(GlobalConstant.dayCode.indexOf(timeCode.substring(0, 2)) + 1, format);
                    });
                });
            });
        }
        writer.write(rows, false);
        setExcelForm(writer, rows, 2);
        writer.close();
        return GlobalConstant.ACCESS_PATH + "/static/OutputExcel" + storePath;
    }

    @Override
    public String roomScheduleToExcel(Integer taskId, Long roomId) {
        TaskInfo taskInfo = taskInfoService.getById(taskId);
        String storePath = "/" + taskInfo.getTaskName() + "/教室课表-" + LocalDate.now() + ".xlsx";
        String targetPath = GlobalConstant.OUTPUT_EXCEL_PATH + storePath;

        FileUtil.del(targetPath);
        ExcelWriter writer = ExcelUtil.getWriter(targetPath);

        Room room = roomMapper.selectById(roomId);
        List<Schedule> scheduleByRoom = scheduleMapper.selectList(new LambdaQueryWrapper<Schedule>().eq(Schedule::getRoomId, roomId));
        //获取教室都有哪些教学班
        Page<TeachingClassCourseDto> page = new Page<>(1, -1);
        List<Long> classIds = scheduleByRoom.stream().map(Schedule::getTeachingClassId).collect(Collectors.toList());
        PageInfo<TeachingClassCourseDto> teachingClassPage = teachingClassService.getTeachingClass(page, taskId, null, null, null, null, null, classIds, null, roomId);
        List<TeachingClassCourseDto> resultList = teachingClassPage.getList();
        //根据最大周次生成课表模板
        int periods = basicRuleService.getMaxPeriodScheme(taskInfo.getSchoolId(), taskInfo.getTerm()).getPeriodSchemes().size();
        String title = "{}教室课表  \n  {}";
        writer.merge(7, StrUtil.format(title, room.getRoomName(), taskInfo.getTermName()), false);
        writer.setRowHeight(0, 40);
        long courseNum = resultList.stream().map(TeachingClassCourseDto::getCourseId).distinct().count();
        long sectionNum = resultList.stream()
                .flatMap(studentTeachingClass -> studentTeachingClass.getSchedules().stream())
                .filter(schedule -> ObjUtil.isNotNull(schedule.getTimeCode())).count();
        String filterTem = "{}上课   共:{}门课程，{}节课   导出时间:{} ";
        String filter = StrUtil.format(filterTem, taskInfo.getWeekSet().equals(0) ? "小周 " : "大周", courseNum, sectionNum, DateUtil.now());
        writer.merge(7, filter, false);
        List<List<String>> rows = ExcelFormatUtil.getScheduleFormat(periods, 7);
        for (TeachingClassCourseDto item : resultList) {
            //以及排好的课节
            List<ScheduleDto> arrangeSchedule = item.getSchedules().stream().filter(scheduleDto -> StrUtil.isNotBlank(scheduleDto.getTimeCode())).collect(Collectors.toList());
            //根据授课类型分组 使用TreeMap来排序
            TreeMap<Integer, List<ScheduleDto>> mapByHourType = arrangeSchedule.stream()
                    .collect(Collectors.groupingBy(ScheduleDto::getHourType, TreeMap::new, Collectors.toList()));
            mapByHourType.forEach((hourType, byHourTypeList) -> {
                //根据timeCode分组
                byHourTypeList.stream().collect(Collectors.groupingBy(ScheduleDto::getTimeCode)).forEach((timeCode, byTimeCodeList) -> {
                    //根据week分组
                    byTimeCodeList.stream().collect(Collectors.groupingBy(ScheduleDto::getWeek)).forEach((week, byWeekList) -> {
                        //单个教学班 单个讲授类型 单个周次  单个节次 对应字符串
//                            String scheduleHour = "{courseName}({hourType}) \n{teachingClassName}\n{buildingName}{roomName}\n{week}"
                        String scheduleHour = "{}({}) \n{}\n{}{}\n{}";
                        //key 0101  节次
                        int index = Integer.parseInt(timeCode.substring(2, 4));
                        String buildingName = byWeekList.get(0).getBuildingName();
                        String roomName = byWeekList.get(0).getRoomName();
                        String weekString = StrUtil.format("[{}]周", WeekUtil.binaryToString(week));
                        String format = StrUtil.format(scheduleHour, item.getCourseName(), GlobalConstant.hourTypeMap.get(hourType)
                                , item.getTeachingClassName(), buildingName, roomName, weekString);
                        rows.get(index).set(GlobalConstant.dayCode.indexOf(timeCode.substring(0, 2)) + 1, format);
                    });
                });
            });
        }
        writer.write(rows, false);
        setExcelForm(writer, rows, 2);
        writer.close();
        return GlobalConstant.ACCESS_PATH + "/static/OutputExcel" + storePath;
    }

    @Override
    public String studentScheduleToExcel(Integer taskId, Long studentId) {
        TaskInfo taskInfo = taskInfoService.getById(taskId);
        String storePath = "/" + taskInfo.getTaskName() + "/" + studentId + "/学生课表-" + LocalDate.now() + ".xlsx";
        String targetPath = GlobalConstant.OUTPUT_EXCEL_PATH + storePath;

        FileUtil.del(targetPath);
        ExcelWriter writer = ExcelUtil.getWriter(targetPath);

        Student student = studentService.getById(studentId);
        List<StudentTeachingClass> resultList = studentService.getSelectedByStudentId(taskId, studentId, 1)
                .stream().filter(studentTeachingClass -> studentTeachingClass.getCourseOrSegment() == 1).collect(Collectors.toList());
        //根据最大周次生成课表模板
        int periods = basicRuleService.getMaxPeriodScheme(taskInfo.getSchoolId(), taskInfo.getTerm()).getPeriodSchemes().size();
        String title = "{}学生课表  \n  {}";
        writer.merge(7, StrUtil.format(title, student.getStudentName(), taskInfo.getTermName()), false);
        writer.setRowHeight(0, 40);
        long courseNum = resultList.stream().map(StudentTeachingClass::getCourseId).distinct().count();
        long sectionNum = resultList.stream()
                .flatMap(studentTeachingClass -> studentTeachingClass.getSchedules().stream())
                .filter(schedule -> ObjUtil.isNotNull(schedule.getTimeCode())).count();
        String filterTem = "{}上课   共:{}门课程，{}节课   导出时间:{} ";
        String filter = StrUtil.format(filterTem, taskInfo.getWeekSet().equals(0) ? "小周 " : "大周", courseNum, sectionNum, DateUtil.now());
        writer.merge(7, filter, false);
        List<List<String>> rows = ExcelFormatUtil.getScheduleFormat(periods, 7);
        for (StudentTeachingClass item : resultList) {
            //以及排好的课节
            List<ScheduleDto> arrangeSchedule = item.getSchedules().stream().filter(scheduleDto -> StrUtil.isNotBlank(scheduleDto.getTimeCode())).collect(Collectors.toList());
            //根据授课类型分组 使用TreeMap来排序
            TreeMap<Integer, List<ScheduleDto>> mapByHourType = arrangeSchedule.stream()
                    .collect(Collectors.groupingBy(ScheduleDto::getHourType, TreeMap::new, Collectors.toList()));
            mapByHourType.forEach((hourType, byHourTypeList) -> {
                //根据timeCode分组
                byHourTypeList.stream().collect(Collectors.groupingBy(ScheduleDto::getTimeCode)).forEach((timeCode, byTimeCodeList) -> {
                    //根据week分组
                    byTimeCodeList.stream().collect(Collectors.groupingBy(ScheduleDto::getWeek)).forEach((week, byWeekList) -> {
                        //单个教学班 单个讲授类型 单个周次  单个节次 对应字符串
//                            String scheduleHour = "{courseName}({hourType}) \n{teachingClassName}\n{buildingName}{roomName}\n{week}"
                        String scheduleHour = "{}({}) \n{}\n{}{}\n{}";
                        //key 0101  节次
                        int index = Integer.parseInt(timeCode.substring(2, 4));
                        String buildingName = byWeekList.get(0).getBuildingName();
                        String roomName = byWeekList.get(0).getRoomName();
                        String weekString = StrUtil.format("[{}]周", WeekUtil.binaryToString(week));
                        String format = StrUtil.format(scheduleHour, item.getCourseName(), GlobalConstant.hourTypeMap.get(hourType)
                                , item.getTeachingClassName(), buildingName, roomName, weekString);
                        rows.get(index).set(GlobalConstant.dayCode.indexOf(timeCode.substring(0, 2)) + 1, format);
                    });
                });
            });
        }
        writer.write(rows, false);
        setExcelForm(writer, rows, 2);
        writer.close();
        return GlobalConstant.ACCESS_PATH + "/static/OutputExcel" + storePath;
    }

    @Override
    public String allScheduleToExcel(Integer taskId, Integer teachingClassType) {
        String modelPath = GlobalConstant.MODEL_EXCEL_PATH + "【模板】课程表信息（大学排课）.xls";

        TaskInfo taskInfo = taskInfoService.getById(taskId);
        String storePath = "/" + taskInfo.getTaskName() + "/syncScheduleToBasic.xlsx";
        String targetPath = GlobalConstant.OUTPUT_EXCEL_PATH + storePath;

        FileUtil.copy(modelPath, targetPath, true);
        ExcelWriter writer = ExcelUtil.getWriter(targetPath);
        writer.setSheet(0);
        writer.passCurrentRow();
        writer.passCurrentRow();

        List<SyncScheduleDto> syncScheduleDtos = scheduleMapper.syncScheduleDto(taskId,teachingClassType);
        //老师
        List<Long> teachingClassIds = syncScheduleDtos.stream().map(SyncScheduleDto::getTeachingClassId).distinct().collect(Collectors.toList());
        Map<Long, String> teachingClassToTeacher = teacherService.getTeacherByTeachingClassIds(teachingClassIds).stream()
                .collect(Collectors.groupingBy(Teacher::getTeachingClassId,
                        Collectors.collectingAndThen(
                                Collectors.mapping(Teacher::getBaseTeacherId, Collectors.toList()),
                                teachers -> teachers.size() > 0 ? teachers.get(0) : null)));

        //课时方案
        Map<String, String> daySchemeMap = ruleWeekDayService.lambdaQuery().eq(RuleWeekDay::getTaskId, taskId).list().stream()
                .collect(Collectors.toMap(RuleWeekDay::getWeekDayCode, RuleWeekDay::getBaseSchemeName));
        List<List<String>> rows = new LinkedList<>();
        List<String> row = null;
        String roomType = "";
        for (SyncScheduleDto syncScheduleDto : syncScheduleDtos) {
            if (ObjUtil.isNull(syncScheduleDto.getTimeCode()) || ObjUtil.isNull(syncScheduleDto.getCourseName())) {
                continue;
            }
            row = new LinkedList<>();
            row.add("全部");
            //上课周数
            String weekNum = IntStream.range(0, syncScheduleDto.getWeek().length())
                    .filter(i -> syncScheduleDto.getWeek().charAt(i) == '1')
                    .mapToObj(i -> String.valueOf(i + 1))
                    .collect(Collectors.joining(","));
            row.add(weekNum);
            //星期
            String week = syncScheduleDto.getTimeCode().substring(0, 2);
            row.add(GlobalConstant.dayNameCodeMap.get(week));
            //课时序号
            row.add(String.valueOf(Integer.parseInt(syncScheduleDto.getTimeCode().substring(2))));
            //教室类型大类1,2,3下的，转换为大类上传
            if ("123".contains(syncScheduleDto.getRoomType().toString().substring(0, 1))) {
                roomType = syncScheduleDto.getRoomType().toString().substring(0, 1);
            }
            row.add(StrUtil.isBlank(roomType) ? syncScheduleDto.getRoomType().toString() : roomType);
            row.add(syncScheduleDto.getRoomName());
            row.add(syncScheduleDto.getCourseName());
            row.add(syncScheduleDto.getTeachingClassName());
            //教师id
            row.add(teachingClassToTeacher.get(syncScheduleDto.getTeachingClassId()));
            row.add("");
            row.add(daySchemeMap.get(week));
            row.add(GlobalConstant.hourTypeMap.get(syncScheduleDto.getHourType()));
            rows.add(row);
        }
        writer.write(rows, false);
        writer.close();
        return "/static/OutputExcel/" + storePath;
    }

    /**
     * 行政班课表
     *
     * @param taskId
     */
    @Override
    public void classScheduleToExcel(Integer taskId, ServletOutputStream out, String classId) {
        TaskInfo taskInfo = taskInfoService.getById(taskId);
        ExcelWriter writer = ExcelUtil.getWriter(true);
        //默认会多一个sheet1页。第一张的话就更新sheetName，不然就新增
        boolean flag = true;
        //教学班 及对应课表
        List<AllScheduleDto> teachingClassSchedule = scheduleMapper.getTeachingClassSchedule(taskId, null, null);
        List<Long> teachingClassIds = teachingClassSchedule.stream().map(AllScheduleDto::getTeachingClassId).distinct().collect(Collectors.toList());
        //根据行政班对课表进行分组
        Map<String, List<AllScheduleDto>> scheduleClassMap = new HashMap<>();
        for (AllScheduleDto schedule : teachingClassSchedule) {
            String[] singleClass = schedule.getToClass().split(",");
            for (String single : singleClass) {
                if (StrUtil.isNotBlank(single)) {
                    scheduleClassMap.computeIfAbsent(single.trim(), k -> new ArrayList<>()).add(schedule);
                }
            }
        }
        //老师
        Map<Long, List<Teacher>> teachingClassToTeacher = teacherService.getTeacherByTeachingClassIds(teachingClassIds).stream()
                .collect(Collectors.groupingBy(Teacher::getTeachingClassId));
        //获取行政班
        List<ClassDto> classList = studentMapper.getClassDto(taskId, null, null, null);
        if (StrUtil.isNotBlank(classId)) {
            classList = classList.stream().filter(classDto -> classDto.getClassId().equals(classId)).collect(Collectors.toList());
        }
        //最大节次
        List<PeriodScheme> periodSchemes = basicRuleService.getMaxPeriodScheme(taskInfo.getSchoolId(), taskInfo.getTerm()).getPeriodSchemes();
        for (ClassDto classDto : classList) {
            if (flag) {
                writer.renameSheet(classDto.getClassName());
                flag = false;
            } else {
                writer.setSheet(classDto.getClassName());
            }
            writer.merge(7, classDto.getClassName() + "课程表", false);
            List<List<String>> rows = ExcelFormatUtil.getScheduleFormat(periodSchemes.size(), 7);

            fillSchedule(rows, scheduleClassMap.getOrDefault(classDto.getClassId(), new ArrayList<>()), teachingClassToTeacher);

            writer.write(rows, false);
            setExcelForm(writer, rows, 1);
        }

        writer.flush(out, true);
        writer.close();
    }

    //填充课表 上课数据
    public void fillSchedule(List<List<String>> rows, List<AllScheduleDto> schedule, Map<Long, List<Teacher>> teachingClassToTeacher) {
        Map<String, List<AllScheduleDto>> byTimeCode = schedule.stream().collect(Collectors.groupingBy(AllScheduleDto::getTimeCode));
        byTimeCode.forEach((timeCode, byTimeCodeList) -> {
            //key 0101  节次
            int index = Integer.parseInt(timeCode.substring(2, 4));
            //单节课数据
            StringJoiner singleSchedule = new StringJoiner("\n");
            for (AllScheduleDto allScheduleDto : byTimeCodeList) {
                //单个教学班数据 {courseName}{单双}\n{teachName}\n{buildingName}{roomName}
                String singleScheduleTemplate = "{} \n {} \n {}{}";
                String courseName = allScheduleDto.getCourseName();
                String teachName = teachingClassToTeacher.getOrDefault(allScheduleDto.getTeachingClassId(), new ArrayList<>()).stream().map(Teacher::getTeacherName).collect(Collectors.joining(","));
                singleSchedule.add(StrUtil.format(singleScheduleTemplate,
                        courseName, teachName, allScheduleDto.getBuildingName(), allScheduleDto.getRoomName()));
            }
            rows.get(index).set(GlobalConstant.dayCode.indexOf(timeCode.substring(0, 2)) + 1, singleSchedule.toString());
        });
    }

    /**
     * 格式化
     *
     * @param writer
     * @param rows
     * @param headLine 表头行数
     */
    private void setExcelForm(ExcelWriter writer, List<List<String>> rows, Integer headLine) {
        int start = 0;
        int end = start + 1;
        writer.setColumnWidth(0, 10);
        for (int colIndex = 1; colIndex < rows.get(0).size(); colIndex++) {
            writer.setColumnWidth(colIndex, 20);
            for (int i = 0; i <= rows.size() + 1; i++) {
                if (i < headLine) {
                    writer.setRowHeight(i, 30);
                } else {
                    writer.setRowHeight(i, 45);
                }
            }
            while (start < rows.size()) {
                if (!"--".equals(rows.get(start).get(colIndex))
                        && end < rows.size()
                        && rows.get(start).get(colIndex).equals(rows.get(end).get(colIndex))) {
                    end++;
                } else {
                    if (end - start > 1) {
                        writer.merge(start + headLine, end + headLine - 1, colIndex, colIndex, rows.get(start).get(colIndex), false);
                    }
                    start = end;
                    end = start + 1;
                }
            }
            start = 0;
            end = start + 1;
        }
    }

    @Override
    public void summaryScheduleToExcel(Integer taskId, ServletOutputStream out, Integer scheduleType) {
        TaskInfo taskInfo = taskInfoService.getById(taskId);
        List<RuleWeekDay> weekDays = basicRuleService.getWeekDays(taskInfo.getSchoolId(), taskInfo.getTerm());
        weekDays.sort(Comparator.comparing(RuleWeekDay::getWeekDayCode));

        //记录该任务所有课时编码
        List<String> timeCodeString = new ArrayList<>();
        String header = scheduleType == 1 ? "班级" : scheduleType == 2 ? "教师" : "场地";
        ExcelWriter writer = ExcelUtil.getWriterWithSheet(header + "总课表");
        //设置表头
        writer.merge(0, 2, 0, 0, header + "/课表", false);
        //记录下周开始的列值 总列值
        int firstCol = 1;
        for (RuleWeekDay weekDay : weekDays) {
            weekDay.getPeriodSchemes().sort(Comparator.comparing(PeriodScheme::getPeriodType));
            int amNum = 0;
            int pmNum = 0;
            //记录下上午下午的列值 用于合并单元格 以及写入节次
            for (PeriodScheme periodScheme : weekDay.getPeriodSchemes()) {
                if (periodScheme.getPeriodType() == 1) {
                    amNum++;
                    writer.writeCellValue(firstCol + amNum - 1, 2, periodScheme.getPeriodName());
                } else if (periodScheme.getPeriodType() == 2) {
                    pmNum++;
                    writer.writeCellValue(firstCol + amNum + pmNum - 1, 2, periodScheme.getPeriodName());
                }
                timeCodeString.add(weekDay.getWeekDayCode() + String.format("%02d", periodScheme.getPeriod()));
            }
            //写入上午下午
            if (amNum == 1) {
                writer.writeCellValue(firstCol, 1, "上午");
            } else if (amNum > 1) {
                writer.merge(1, 1, firstCol, firstCol + amNum - 1, "上午", false);
            }
            if (pmNum == 1) {
                writer.writeCellValue(firstCol + amNum, 1, "下午");
            } else if (pmNum > 1) {
                writer.merge(1, 1, firstCol + amNum, firstCol + amNum + pmNum - 1, "下午", false);
            }

            //写入星期
            if (amNum + pmNum == 1) {
                writer.writeCellValue(firstCol, 0, GlobalConstant.dayNameCodeMap.get(weekDay.getWeekDayCode()));
            } else {
                writer.merge(0, 0, firstCol, firstCol + amNum + pmNum - 1, GlobalConstant.dayNameCodeMap.get(weekDay.getWeekDayCode()), false);
            }
            firstCol = firstCol + amNum + pmNum;
        }

        writer.setColumnWidth(-1, 30);
        writer.passRows(3);
        if (scheduleType == 1) {
            fillClassSummarySchedule(taskId, timeCodeString, writer);
        } else if (scheduleType == 2) {
            fillTeacherSummarySchedule(taskId, timeCodeString, writer);
        } else if (scheduleType == 3) {
            fillRoomSummarySchedule(taskId, timeCodeString, writer);
        }
        writer.setDefaultRowHeight(60);
        writer.setRowHeight(0, 30);
        writer.setRowHeight(1, 30);
        writer.setRowHeight(2, 30);

        writer.flush(out, true);
        writer.close();
    }

    public void fillClassSummarySchedule(Integer taskId, List<String> timeCodeString, ExcelWriter writer) {
        //教学班 及对应课表
        List<AllScheduleDto> teachingClassSchedule = scheduleMapper.getTeachingClassSchedule(taskId, null, null);
        List<Long> teachingClassIds = teachingClassSchedule.stream().map(AllScheduleDto::getTeachingClassId).distinct().collect(Collectors.toList());
        //根据行政班对课表进行分组
        Map<String, List<AllScheduleDto>> scheduleClassMap = new HashMap<>();
        for (AllScheduleDto schedule : teachingClassSchedule) {
            String[] singleClass = schedule.getToClass().split(",");
            for (String single : singleClass) {
                if (StrUtil.isNotBlank(single)) {
                    scheduleClassMap.computeIfAbsent(single.trim(), k -> new ArrayList<>()).add(schedule);
                }
            }
        }
        //老师
        Map<Long, List<Teacher>> teachingClassToTeacher = teacherService.getTeacherByTeachingClassIds(teachingClassIds).stream()
                .collect(Collectors.groupingBy(Teacher::getTeachingClassId));
        //获取行政班
        List<ClassDto> classList = studentMapper.getClassDto(taskId, null, null, null);

        for (ClassDto classDto : classList) {
            List<AllScheduleDto> scheduleList = scheduleClassMap.get(classDto.getClassId());
            if (scheduleList == null) {
                continue;
            }
            List<String> row = Arrays.asList(new String[timeCodeString.size() + 1]);
            //首列  设置行政班名称
            row.set(0, classDto.getClassName());
            scheduleList.stream().collect(Collectors.groupingBy(AllScheduleDto::getTimeCode))
                    .forEach((timeCode, byTimeCodeList) -> {
                        //单节课数据
                        StringJoiner singleSchedule = new StringJoiner("\n");
                        for (AllScheduleDto allScheduleDto : byTimeCodeList) {
                            //单个教学班数据 {courseName}{单双}\n{teachName}\n{buildingName}{roomName}
                            String singleScheduleTemplate = "{} \n {} \n {}{}";
                            String courseName = allScheduleDto.getCourseName();
                            String teachName = teachingClassToTeacher.getOrDefault(allScheduleDto.getTeachingClassId(), new ArrayList<>()).stream().map(Teacher::getTeacherName).collect(Collectors.joining(","));
                            singleSchedule.add(StrUtil.format(singleScheduleTemplate,
                                    courseName, teachName, allScheduleDto.getBuildingName(), allScheduleDto.getRoomName()));
                        }
                        row.set(timeCodeString.indexOf(timeCode) + 1, singleSchedule.toString());
                    });
            writer.writeRow(row);
        }
    }

    public void fillTeacherSummarySchedule(Integer taskId, List<String> timeCodeString, ExcelWriter writer) {
        //教学班 及对应课表
        List<AllScheduleDto> teachingClassSchedule = scheduleMapper.getTeachingClassSchedule(taskId, null, null);
        if (CollUtil.isEmpty(teachingClassSchedule)) {
            return;
        }
        List<Long> teachingClassIds = teachingClassSchedule.stream().map(AllScheduleDto::getTeachingClassId).distinct().collect(Collectors.toList());
        //老师
        Map<Long, List<Teacher>> teachingClassToTeacher = teacherService.getTeacherByTeachingClassIds(teachingClassIds).stream()
                .collect(Collectors.groupingBy(Teacher::getTeachingClassId));
        //获取行政班
        Map<String, String> classMap = studentMapper.getClassDto(taskId, null, null, null)
                .stream().collect(Collectors.toMap(ClassDto::getClassId, ClassDto::getClassName));
        //确定每节课表所对应的教师。 遍历课表 分组
        Map<String, List<AllScheduleDto>> scheduleTeacherMap = new HashMap<>();
        for (AllScheduleDto schedule : teachingClassSchedule) {
            teachingClassToTeacher.getOrDefault(schedule.getTeachingClassId(), new ArrayList<>()).forEach(teacher -> {
                scheduleTeacherMap.computeIfAbsent(teacher.getTeacherName() + teacher.getBaseTeacherId(), k -> new ArrayList<>()).add(schedule);
            });
        }
        //遍历分组 生成excel
        for (Map.Entry<String, List<AllScheduleDto>> entry : scheduleTeacherMap.entrySet()) {
            List<AllScheduleDto> scheduleList = entry.getValue();
            if (CollUtil.isEmpty(scheduleList)) {
                continue;
            }
            List<String> row = Arrays.asList(new String[timeCodeString.size() + 1]);
            //首列  设置行政班名称
            row.set(0, entry.getKey());
            scheduleList.stream().collect(Collectors.groupingBy(AllScheduleDto::getTimeCode))
                    .forEach((timeCode, byTimeCodeList) -> {
                        //单节课数据
                        StringJoiner singleSchedule = new StringJoiner("\n");
                        for (AllScheduleDto allScheduleDto : byTimeCodeList) {
                            //单个教学班数据 {courseName}{单双}\n{行政班名}\n{buildingName}{roomName}
                            String singleScheduleTemplate = "{} \n {} \n {}{}";
                            String courseName = allScheduleDto.getCourseName();
                            //多个行政班情况
                            StringJoiner className = new StringJoiner(",");
                            for (String s : allScheduleDto.getToClass().split(",")) {
                                if (StrUtil.isNotBlank(classMap.get(s))) {
                                    className.add(classMap.get(s));
                                }
                            }
                            singleSchedule.add(StrUtil.format(singleScheduleTemplate,
                                    courseName, className.toString(), allScheduleDto.getBuildingName(), allScheduleDto.getRoomName()));
                        }
                        row.set(timeCodeString.indexOf(timeCode) + 1, singleSchedule.toString());
                    });
            writer.writeRow(row);
        }
    }

    public void fillRoomSummarySchedule(Integer taskId, List<String> timeCodeString, ExcelWriter writer) {
        //教学班 及对应课表
        List<AllScheduleDto> teachingClassSchedule = scheduleMapper.getTeachingClassSchedule(taskId, null, null);
        if (CollUtil.isEmpty(teachingClassSchedule)) {
            return;
        }
        List<Long> teachingClassIds = teachingClassSchedule.stream().map(AllScheduleDto::getTeachingClassId).distinct().collect(Collectors.toList());
        //老师
        Map<Long, List<Teacher>> teachingClassToTeacher = teacherService.getTeacherByTeachingClassIds(teachingClassIds).stream()
                .collect(Collectors.groupingBy(Teacher::getTeachingClassId));
        //获取行政班
        Map<String, String> classMap = studentMapper.getClassDto(taskId, null, null, null)
                .stream().collect(Collectors.toMap(ClassDto::getClassId, ClassDto::getClassName));
        //根据room分组
        Map<Long, List<AllScheduleDto>> scheduleRoomMap = teachingClassSchedule.stream().collect(Collectors.groupingBy(AllScheduleDto::getRoomId));
        //遍历分组 生成excel
        for (Map.Entry<Long, List<AllScheduleDto>> entry : scheduleRoomMap.entrySet()) {
            List<AllScheduleDto> scheduleList = entry.getValue();
            if (CollUtil.isEmpty(scheduleList)) {
                continue;
            }
            List<String> row = Arrays.asList(new String[timeCodeString.size() + 1]);
            //首列  设置行政班名称
            row.set(0, scheduleList.get(0).getBuildingName() + scheduleList.get(0).getRoomName());
            scheduleList.stream().collect(Collectors.groupingBy(AllScheduleDto::getTimeCode))
                    .forEach((timeCode, byTimeCodeList) -> {
                        //单节课数据
                        StringJoiner singleSchedule = new StringJoiner("\n");
                        for (AllScheduleDto allScheduleDto : byTimeCodeList) {
                            //单个教学班数据 {courseName}{单双}\n{行政班名}\n{teacherName}
                            String singleScheduleTemplate = "{} \n {} \n {}";
                            String courseName = allScheduleDto.getCourseName();
                            //多个行政班情况
                            StringJoiner className = new StringJoiner(",");
                            for (String s : allScheduleDto.getToClass().split(",")) {
                                if (StrUtil.isNotBlank(classMap.get(s))) {
                                    className.add(classMap.get(s));
                                }
                            }
                            String teachName = teachingClassToTeacher.getOrDefault(allScheduleDto.getTeachingClassId(), new ArrayList<>()).stream().map(Teacher::getTeacherName).collect(Collectors.joining(","));
                            singleSchedule.add(StrUtil.format(singleScheduleTemplate,
                                    courseName, className.toString(), teachName));
                        }
                        row.set(timeCodeString.indexOf(timeCode) + 1, singleSchedule.toString());
                    });
            writer.writeRow(row);
        }
    }


}
